package jwt

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"git.myscrm.cn/golang/common/uuid"
	"time"

	"github.com/dgrijalva/jwt-go"
)

// 一些常量
var (
	TokenExpired     = errors.New("token is expired")
	TokenNotValidYet = errors.New("token not active yet")
	TokenMalformed   = errors.New("that's not even a token")
	TokenInvalid     = errors.New("couldn't handle this token")
)

const SignKey = "7264daa3-0cdb-11ed-8246-043f72e4f15a"

type JWT struct {
	SigningKey []byte
}

var DefaultJWT = &JWT{[]byte(SignKey)}

type CustomClaims struct {
	//租户ID
	TenantCode string `json:"tenant_code"`
	//租户名称
	TenantName string `json:"tenant_name"`
	//全局用户编号
	GlobalUserId string `json:"global_user_id"`
	// 租户用户编号
	TenantUserId string `json:"tenant_user_id"`
	// 租户用户code
	TenantUserCode string `json:"tenant_user_code"`
	// 租户用户名
	TenantUserName string `json:"tenant_user_name"`
	// 手机号
	MobilePhone string `json:"mobile_phone"`
	// gfyx_user表的主键id
	UserId int64 `json:"user_id"`
	//token
	Tk        string `json:"tk"`
	Timestamp int64  `json:"timestamp"`
	jwt.StandardClaims
}

// 服务间鉴权调用的payload
type ServiceAuthClaims struct {
	//appid应用标志
	A string `json:"a"`
	//扩展字段
	E string `json:"e"`
	jwt.StandardClaims
}

// CreateToken
// 创建token
func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
	token := jwt.New(jwt.SigningMethodHS256)
	token.Claims = claims
	res, err := token.SignedString(j.SigningKey)
	return res, err
}

// ParseToken
// 解析token
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, TokenInvalid
}

// 解析服务间鉴权token
func (j *JWT) ParseServiceAuthToken(tokenString string, signingKey string) (*ServiceAuthClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &ServiceAuthClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(signingKey), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*ServiceAuthClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, TokenInvalid
}

// 生成服务间调用jwt（测试用）
func (j *JWT) GenerateServiceAuthToken(appId, extend string, signKey string) (string, error) {
	// todo 从阿波罗取
	//vars gfyxSignKey = stark.Configer.GetStringValue("gfyx_model_main_key", "")
	j.SigningKey = []byte(signKey)
	claims := ServiceAuthClaims{
		appId,
		extend,
		jwt.StandardClaims{
			NotBefore: time.Now().Unix() - 10,
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: time.Now().Unix() + 3600,
			Issuer:    "ycg-mycaigou",
			Id:        uuid.GetUUID(),
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(j.SigningKey)
	return tokenString, err
}

// RefreshToken
// 更新token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}

	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})

	if err != nil {
		return "", err
	}

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
		return j.CreateToken(*claims)
	}

	return "", TokenInvalid
}

// GenerateToken
// GenerateToken 生成jwt token
func (j *JWT) GenerateToken(token, tenantCode, globalUserId, tenantUserId, tenantUserCode, tenantUserName, mobilePhone, tenantName string, userId int64) (string, error) {
	claims := CustomClaims{
		tenantCode,
		tenantName,
		globalUserId,
		tenantUserId,
		tenantUserCode,
		tenantUserName,
		mobilePhone,
		userId,
		token,
		time.Now().Unix(),
		jwt.StandardClaims{
			NotBefore: time.Now().Unix() - 1000,
			ExpiresAt: time.Now().Unix() + 3600*24*7,
			Issuer:    "ycg-gfyx",
		},
	}

	token, err := j.CreateToken(claims)
	return token, err
}

// HmacSha256
// hmacsha256验证
func HmacSha256(src, key string) (string, error) {
	m := hmac.New(sha256.New, []byte(key))
	_, err := m.Write([]byte(src))
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(m.Sum(nil)), nil
}
